﻿using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;

namespace Framework.WebApi.IPRate;

public static class IPRateExtension
{
	/// <summary>
	/// IP和速率限制
	/// </summary>
	/// <param name="serviceCollection"></param>
	public static void AddIPRate(this IServiceCollection serviceCollection)
	{
		//从appsettings.json中加载常规配置，IpRateLimiting与配置文件中节点对应
		serviceCollection.Configure<IpRateLimitOptions>(config: App.Configuration.GetSection("IpRateLimiting"));

		//从appsettings.json中加载Ip规则
		serviceCollection.Configure<IpRateLimitPolicies>(App.Configuration.GetSection("IpRateLimitPolicies"));
		//注入计数器和规则存储
		serviceCollection.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
		serviceCollection.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
		//配置（解析器、计数器密钥生成器）
		serviceCollection.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
		serviceCollection.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();

		// 速率限制
		serviceCollection.AddRateLimiter(_ => _
		// 固定窗口限流器，下面设置表示在这个窗口内（12秒），可以最多有6个请求，4个会被处理，2个则在排队，其他的则会在一定时间后拒绝返回
		.AddFixedWindowLimiter(policyName: "fixed", options =>
		{
			options.PermitLimit = 4;    // 每窗口允许最多请求数,设为4
			options.Window = TimeSpan.FromSeconds(12);  // 每窗口的时间，单位秒
			options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;    // 排队请求的处理顺序,优先处理先来的请求
			options.QueueLimit = 2; // 当窗口请求数达到最大时，后续请求会进入排队，用于设置队列的大小，设为2
		}));
	}

	/// <summary>
	/// 配置IP速率限制中间件
	/// </summary>
	/// <param name="webApplication"></param>
	public static void UseIPRate(this IApplicationBuilder webApplication)
	{
		//启用客户端IP限制速率
		webApplication.UseIpRateLimiting();
		webApplication.UseRateLimiter();
	}
}